home *** CD-ROM | disk | FTP | other *** search
/ Windows Game Programming for Dummies (2nd Edition) / WinGamProgFD.iso / pc / DirectX SDK / DXSDK / samples / Multimedia / DirectPlay / DataRelay / datarelay.cpp next >
Encoding:
C/C++ Source or Header  |  2001-10-31  |  65.1 KB  |  1,690 lines

  1. //----------------------------------------------------------------------------
  2. // File: DataRelay.cpp
  3. //
  4. // Desc: The main game file for the DataRelay sample.  It connects 
  5. //       players together with two dialog boxes to prompt users on the 
  6. //       connection settings to join or create a session. After the user 
  7. //       connects to a sesssion, the sample displays a multiplayer stage. 
  8. // 
  9. //       This sample uses DirectPlay to process high volumes of incoming 
  10. //       data on a seperate worker while avoiding copying the incoming packets
  11. //       by using the ReturnBuffer() call.  It also shows some basics of 
  12. //       thread synconization between the worker thread and the DirectPlay
  13. //       message handler thread pool.
  14. //
  15. // Copyright (c) 1999-2001 Microsoft Corp. All rights reserved.
  16. //-----------------------------------------------------------------------------
  17. #define STRICT
  18. #include <windows.h>
  19. #include <basetsd.h>
  20. #include <process.h>
  21. #include <mmsystem.h>
  22. #include <dxerr8.h>
  23. #include <dplay8.h>
  24. #include <dplobby8.h>
  25. #include <stdio.h>
  26. #include <assert.h>
  27. #include "NetConnect.h"
  28. #include "DXUtil.h"
  29. #include "resource.h"
  30.  
  31.  
  32.  
  33.  
  34. //-----------------------------------------------------------------------------
  35. // Player context locking defines
  36. //-----------------------------------------------------------------------------
  37. CRITICAL_SECTION g_csPlayerContext;
  38. #define PLAYER_LOCK()                   EnterCriticalSection( &g_csPlayerContext ); 
  39. #define PLAYER_ADDREF( pPlayerInfo )    if( pPlayerInfo ) pPlayerInfo->lRefCount++;
  40. #define PLAYER_RELEASE( pPlayerInfo )   if( pPlayerInfo ) { pPlayerInfo->lRefCount--; if( pPlayerInfo->lRefCount <= 0 ) SAFE_DELETE( pPlayerInfo ); } pPlayerInfo = NULL;
  41. #define PLAYER_UNLOCK()                 LeaveCriticalSection( &g_csPlayerContext );
  42.  
  43.  
  44. //-----------------------------------------------------------------------------
  45. // Defines, and constants
  46. //-----------------------------------------------------------------------------
  47. #define DPLAY_SAMPLE_KEY        TEXT("Software\\Microsoft\\DirectX DirectPlay Samples")
  48. #define MAX_PLAYER_NAME         14
  49. #define TIMERID_NETWORK         0
  50. #define TIMERID_STATS           1
  51. #define WM_APP_UPDATE_TARGETS   (WM_APP + 0)
  52. #define WM_APP_APPEND_TEXT      (WM_APP + 1)
  53.  
  54. // This GUID allows DirectPlay to find other instances of the same game on
  55. // the network.  So it must be unique for every game, and the same for 
  56. // every instance of that game.  // {BA214178-AAE6-4ea6-84E0-65CE36F84479}
  57. GUID g_guidApp = { 0xba214178, 0xaae6, 0x4ea6, { 0x84, 0xe0, 0x65, 0xce, 0x36, 0xf8, 0x44, 0x79 } };
  58.  
  59. struct APP_PLAYER_INFO
  60. {
  61.     LONG  lRefCount;                        // Ref count so we can cleanup when all threads 
  62.                                             // are done w/ this object
  63.     DPNID dpnidPlayer;                      // DPNID of player
  64.     TCHAR strPlayerName[MAX_PLAYER_NAME];  // Player name
  65.     DWORD dwFlags;                          // Player flags
  66.     APP_PLAYER_INFO* pNext;
  67.     APP_PLAYER_INFO* pPrev;
  68. };
  69.  
  70.  
  71.  
  72.  
  73. //-----------------------------------------------------------------------------
  74. // App specific DirectPlay messages and structures 
  75. //-----------------------------------------------------------------------------
  76. #define GAME_MSGID_GAMEPACKET       1
  77.  
  78. // Change compiler pack alignment to be BYTE aligned, and pop the current value
  79. #pragma pack( push, 1 )
  80.  
  81. struct GAMEMSG_GENERIC
  82. {
  83.     DWORD dwType;
  84.     DWORD dwPacketId;
  85. };
  86.  
  87. #define GAMEMSG_GENERIC_SIZE 8
  88.  
  89. struct GAMEMSG_DATA_512 : GAMEMSG_GENERIC
  90. {
  91.     BYTE pBuffer[512-GAMEMSG_GENERIC_SIZE];
  92. };
  93.  
  94. struct GAMEMSG_DATA_256 : GAMEMSG_GENERIC
  95. {
  96.     BYTE pBuffer[256-GAMEMSG_GENERIC_SIZE];
  97. };
  98.  
  99. struct GAMEMSG_DATA_128 : GAMEMSG_GENERIC
  100. {
  101.     BYTE pBuffer[128-GAMEMSG_GENERIC_SIZE];
  102. };
  103.  
  104. struct GAMEMSG_DATA_64 : GAMEMSG_GENERIC
  105. {
  106.     BYTE pBuffer[64-GAMEMSG_GENERIC_SIZE];
  107. };
  108.  
  109. struct GAMEMSG_DATA_32 : GAMEMSG_GENERIC
  110. {
  111.     BYTE pBuffer[32-GAMEMSG_GENERIC_SIZE];
  112. };
  113.  
  114. struct GAMEMSG_DATA_16 : GAMEMSG_GENERIC
  115. {
  116.     BYTE pBuffer[16-GAMEMSG_GENERIC_SIZE];
  117. };
  118.  
  119. #define DATA_TYPE_NETPACKET_RECIEVE       1
  120. #define DATA_TYPE_NETPACKET_SENT          2
  121. #define DATA_TYPE_NETPACKET_TIMEOUT       3
  122.  
  123. struct GAMEMSG_DATA_NODE
  124. {
  125.     GAMEMSG_GENERIC*    pDataMsg;
  126.     DWORD               dwPacketId;
  127.     APP_PLAYER_INFO*    pPlayerFrom; 
  128.     DWORD               dwReceiveDataSize;
  129.     DPNHANDLE           hBufferHandle;
  130.     DWORD               dwType;
  131.     GAMEMSG_DATA_NODE*  pNext;
  132.     GAMEMSG_DATA_NODE*  pPrev;
  133. };
  134.  
  135. // Pop the old pack alignment
  136. #pragma pack( pop )
  137.  
  138.  
  139.  
  140.  
  141. //-----------------------------------------------------------------------------
  142. // Global variables
  143. //-----------------------------------------------------------------------------
  144. TCHAR              g_strAppName[256]          = TEXT("DataRelay");
  145. IDirectPlay8Peer*  g_pDP                      = NULL;   // DirectPlay peer object
  146. CNetConnectWizard* g_pNetConnectWizard        = NULL;   // Connection wizard
  147. IDirectPlay8LobbiedApplication* g_pLobbiedApp = NULL;   // DirectPlay lobbied app 
  148. BOOL               g_bWasLobbyLaunched        = FALSE;   // TRUE if lobby launched
  149. HINSTANCE          g_hInst                    = NULL;   // HINST of app
  150. HWND               g_hDlg                     = NULL;   // HWND of main dialog
  151. HRESULT            g_hrDialog;                          // Exit code for app 
  152. TCHAR              g_strLocalPlayerName[MAX_PATH];      // Local player name
  153. TCHAR              g_strSessionName[MAX_PATH];          // Session name
  154. TCHAR              g_strPreferredProvider[MAX_PATH];    // Provider string
  155. HANDLE             g_hDPDataAvailEvent        = NULL;   // Signaled if there is data to process
  156. HANDLE             g_hShutdownEvent           = NULL;   // Signaled if shutting down
  157. DWORD              g_dwPacketId               = 0;      // Packet id counter
  158. APP_PLAYER_INFO*   g_pTargetPlayer            = NULL;   // The current target player
  159. BOOL               g_bSendingData             = FALSE;  // TRUE if sending data
  160. DPNID              g_dpnidLocalPlayer         = 0;      // DPNID of local player
  161. DWORD              g_dwDataRecieved           = 0;      // Amount of data recieved 
  162. DWORD              g_dwDataSent               = 0;      // Amount of data sent
  163. DWORD              g_dwSendTimeout            = 0;      // Length of send timeout
  164. DWORD              g_dwTimeBetweenSends       = 0;      // Time between packet sends
  165. DWORD              g_dwSendSize               = 0;      // Size in bytes of packet
  166. UINT               g_dwProcessNetDataThreadID = 0;      // Worker thread ID
  167. HANDLE             g_hProcessNetDataThread    = NULL;   // Worker thread handle
  168. APP_PLAYER_INFO    g_PlayerHead;                        // Linked list of players connected
  169. GAMEMSG_DATA_NODE  g_DataHead;                          // Linked list of data to process
  170. CRITICAL_SECTION   g_csPlayerList;                      // CS for g_PlayerHead
  171. CRITICAL_SECTION   g_csDataList;                        // CS for g_DataHead
  172. APP_PLAYER_INFO*   g_pConnInfoTargetPlayer    = NULL;   // The current target for Connection Info
  173.  
  174.  
  175.  
  176.  
  177. //-----------------------------------------------------------------------------
  178. // Function-prototypes
  179. //-----------------------------------------------------------------------------
  180. HRESULT WINAPI   DirectPlayMessageHandler( PVOID pvUserContext, DWORD dwMessageId, PVOID pMsgBuffer );
  181. HRESULT WINAPI   DirectPlayLobbyMessageHandler( PVOID pvUserContext, DWORD dwMessageId, PVOID pMsgBuffer );
  182. INT_PTR CALLBACK SampleDlgProc( HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam );
  183. UINT WINAPI     ProcessNetDataProc( LPVOID lpParameter );
  184. HRESULT InitDirectPlay();
  185. HRESULT OnInitDialog( HWND hDlg );
  186. VOID    FillTargetCombo( HWND hDlg );
  187. VOID    FillOneTimeCombos( HWND hDlg );
  188. HRESULT LinkPlayer( DPNID dpnid, APP_PLAYER_INFO** ppPlayerInfo );
  189. HRESULT SendNetworkData();
  190. HRESULT ProcessData();
  191. VOID    AppendTextToEditControl( HWND hDlg, TCHAR* strNewLogLine );
  192. VOID    ReadCombos( HWND hDlg );
  193. VOID    UpdateSendQueueInfo( HWND hDlg );
  194. VOID    UpdateStats( HWND hDlg );
  195.  
  196.  
  197.  
  198.  
  199. //-----------------------------------------------------------------------------
  200. // Name: WinMain()
  201. // Desc: Entry point for the application.  Since we use a simple dialog for 
  202. //       user interaction we don't need to pump messages.
  203. //-----------------------------------------------------------------------------
  204. INT APIENTRY WinMain( HINSTANCE hInst, HINSTANCE hPrevInst, 
  205.                       LPSTR pCmdLine, INT nCmdShow )
  206. {
  207.     HRESULT hr;
  208.     BOOL    bConnectSuccess     = FALSE;
  209.     HKEY    hDPlaySampleRegKey;
  210.  
  211.     // Create events and critical sections
  212.     g_hDPDataAvailEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); 
  213.     g_hShutdownEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); 
  214.     InitializeCriticalSection( &g_csPlayerList );
  215.     InitializeCriticalSection( &g_csDataList );
  216.     InitializeCriticalSection( &g_csPlayerContext );
  217.  
  218.     g_hInst = hInst;
  219.  
  220.     // Init circular linked list
  221.     ZeroMemory( &g_PlayerHead, sizeof(APP_PLAYER_INFO) );
  222.     g_PlayerHead.pNext = &g_PlayerHead;
  223.     g_PlayerHead.pPrev = &g_PlayerHead;
  224.     g_PlayerHead.dpnidPlayer = DPNID_ALL_PLAYERS_GROUP;
  225.     _tcscpy( g_PlayerHead.strPlayerName, TEXT("Everyone") );
  226.  
  227.     // Init circular linked list
  228.     ZeroMemory( &g_DataHead, sizeof(GAMEMSG_DATA_NODE) );
  229.     g_DataHead.pNext = &g_DataHead;
  230.     g_DataHead.pPrev = &g_DataHead;  
  231.  
  232.     // Read persistent state information from registry
  233.     RegCreateKeyEx( HKEY_CURRENT_USER, DPLAY_SAMPLE_KEY, 0, NULL,
  234.                     REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, 
  235.                     &hDPlaySampleRegKey, NULL );
  236.     DXUtil_ReadStringRegKey( hDPlaySampleRegKey, TEXT("Player Name"), 
  237.                              g_strLocalPlayerName, MAX_PATH, TEXT("TestPlayer") );
  238.     DXUtil_ReadStringRegKey( hDPlaySampleRegKey, TEXT("Session Name"), 
  239.                              g_strSessionName, MAX_PATH, TEXT("TestGame") );
  240.     DXUtil_ReadStringRegKey( hDPlaySampleRegKey, TEXT("Preferred Provider"), 
  241.                              g_strPreferredProvider, MAX_PATH, TEXT("DirectPlay8 TCP/IP Service Provider") );
  242.  
  243.     // Init COM so we can use CoCreateInstance
  244.     CoInitializeEx( NULL, COINIT_MULTITHREADED );
  245.     
  246.     // Create helper class
  247.     g_pNetConnectWizard = new CNetConnectWizard( hInst, NULL, g_strAppName, &g_guidApp );
  248.  
  249.     if( FAILED( hr = InitDirectPlay() ) )
  250.     {
  251.         DXTRACE_ERR( TEXT("InitDirectPlay"), hr );
  252.         MessageBox( NULL, TEXT("Failed initializing IDirectPlay8Peer. ")
  253.                     TEXT("The sample will now quit."),
  254.                     TEXT("DirectPlay Sample"), MB_OK | MB_ICONERROR );
  255.         return FALSE;
  256.     }
  257.  
  258.     // Check if we were launched from a lobby client
  259.     if( g_bWasLobbyLaunched && g_pNetConnectWizard->HaveConnectionSettingsFromLobby() )
  260.     {
  261.         // If were lobby launched then DPL_MSGID_CONNECT has already been
  262.         // handled, so we can just tell the wizard to connect to the lobby
  263.         // that has sent us a DPL_MSGID_CONNECT msg.
  264.         if( FAILED( hr = g_pNetConnectWizard->ConnectUsingLobbySettings() ) )
  265.         {
  266.             DXTRACE_ERR( TEXT("ConnectUsingLobbySettings"), hr );
  267.             MessageBox( NULL, TEXT("Failed to connect using lobby settings. ")
  268.                         TEXT("The sample will now quit."),
  269.                         TEXT("DirectPlay Sample"), MB_OK | MB_ICONERROR );
  270.  
  271.             bConnectSuccess = FALSE;
  272.         }
  273.         else
  274.         {
  275.             // Read information from g_pNetConnectWizard
  276.             _tcsncpy( g_strLocalPlayerName, g_pNetConnectWizard->GetPlayerName(), MAX_PLAYER_NAME );
  277.             g_strLocalPlayerName[MAX_PLAYER_NAME-1]=0;
  278.  
  279.             bConnectSuccess = TRUE; 
  280.         }
  281.     }
  282.     else
  283.     {
  284.         // If not lobby launched, prompt the user about the network 
  285.         // connection and which session they would like to join or 
  286.         // if they want to create a new one.
  287.  
  288.         // Setup connection wizard
  289.         g_pNetConnectWizard->SetPlayerName( g_strLocalPlayerName );
  290.         g_pNetConnectWizard->SetSessionName( g_strSessionName );
  291.         g_pNetConnectWizard->SetPreferredProvider( g_strPreferredProvider );
  292.  
  293.         // Do the connection wizard
  294.         hr = g_pNetConnectWizard->DoConnectWizard( FALSE );        
  295.         if( FAILED( hr ) ) 
  296.         {
  297.             DXTRACE_ERR( TEXT("DoConnectWizard"), hr );
  298.             MessageBox( NULL, TEXT("Multiplayer connect failed. ")
  299.                         TEXT("The sample will now quit."),
  300.                         TEXT("DirectPlay Sample"), MB_OK | MB_ICONERROR );
  301.             bConnectSuccess = FALSE;
  302.         } 
  303.         else if( hr == NCW_S_QUIT ) 
  304.         {
  305.             // The user canceled the Multiplayer connect, so quit 
  306.             bConnectSuccess = FALSE;
  307.         }
  308.         else
  309.         {
  310.             bConnectSuccess = TRUE; 
  311.  
  312.             // Read information from g_pNetConnectWizard
  313.             _tcsncpy( g_strLocalPlayerName, g_pNetConnectWizard->GetPlayerName(), MAX_PLAYER_NAME );
  314.             g_strLocalPlayerName[MAX_PLAYER_NAME-1]=0;
  315.             _tcscpy( g_strSessionName, g_pNetConnectWizard->GetSessionName() );
  316.             _tcscpy( g_strPreferredProvider, g_pNetConnectWizard->GetPreferredProvider() );
  317.  
  318.             // Write information to the registry
  319.             DXUtil_WriteStringRegKey( hDPlaySampleRegKey, TEXT("Player Name"), g_strLocalPlayerName );
  320.             DXUtil_WriteStringRegKey( hDPlaySampleRegKey, TEXT("Session Name"), g_strSessionName );
  321.             DXUtil_WriteStringRegKey( hDPlaySampleRegKey, TEXT("Preferred Provider"), g_strPreferredProvider );
  322.         }
  323.     }
  324.  
  325.     if( bConnectSuccess )
  326.     {
  327.         // For this sample, we just start a simple dialog box game.
  328.         g_hrDialog = S_OK;
  329.         DialogBox( hInst, MAKEINTRESOURCE(IDD_MAIN_GAME), NULL, 
  330.                    (DLGPROC) SampleDlgProc );
  331.     }
  332.  
  333.     // Close down process data thread
  334.     SetEvent( g_hShutdownEvent );
  335.     WaitForSingleObject( g_hProcessNetDataThread, INFINITE );
  336.     CloseHandle( g_hProcessNetDataThread );
  337.  
  338.     if( FAILED( g_hrDialog ) )
  339.     {
  340.         if( g_hrDialog == DPNERR_CONNECTIONLOST )
  341.         {
  342.             MessageBox( NULL, TEXT("The DirectPlay session was lost. ")
  343.                         TEXT("The sample will now quit."),
  344.                         TEXT("DirectPlay Sample"), MB_OK | MB_ICONERROR );
  345.         }
  346.         else
  347.         {
  348.             DXTRACE_ERR( TEXT("DialogBox"), g_hrDialog );
  349.             MessageBox( NULL, TEXT("An error occured during the game. ")
  350.                         TEXT("The sample will now quit."),
  351.                         TEXT("DirectPlay Sample"), MB_OK | MB_ICONERROR );
  352.         }
  353.     }
  354.  
  355.     // Cleanup DirectPlay and helper classes
  356.     g_pNetConnectWizard->Shutdown();
  357.  
  358.     if( g_pDP )
  359.     {
  360.         g_pDP->Close(0);
  361.         SAFE_RELEASE( g_pDP );
  362.     }
  363.  
  364.     if( g_pLobbiedApp )
  365.     {
  366.         g_pLobbiedApp->Close( 0 );
  367.         SAFE_RELEASE( g_pLobbiedApp );
  368.     }    
  369.  
  370.     // Don't delete the wizard until we know that 
  371.     // DirectPlay is out of its message handlers.
  372.     // This will be true after Close() has been called. 
  373.     SAFE_DELETE( g_pNetConnectWizard );
  374.  
  375.     // Cleanup circular linked list, g_PlayerHead
  376.     {
  377.         EnterCriticalSection( &g_csPlayerList );
  378.         APP_PLAYER_INFO* pNode = g_PlayerHead.pNext;
  379.         APP_PLAYER_INFO* pDeleteNode;
  380.         while( pNode != &g_PlayerHead ) 
  381.         {
  382.             pDeleteNode = pNode;
  383.             pNode = pNode->pNext;
  384.             SAFE_DELETE( pDeleteNode );
  385.         }
  386.         LeaveCriticalSection( &g_csPlayerList );
  387.     }
  388.  
  389.     // Cleanup circular linked list, g_DataHead
  390.     {
  391.         EnterCriticalSection( &g_csDataList );
  392.         GAMEMSG_DATA_NODE* pNode = g_DataHead.pNext;
  393.         GAMEMSG_DATA_NODE* pDeleteNode;
  394.         while( pNode != &g_DataHead ) 
  395.         {
  396.             pDeleteNode = pNode;
  397.             pNode = pNode->pNext;
  398.             SAFE_DELETE( pDeleteNode );
  399.         }
  400.         LeaveCriticalSection( &g_csDataList );
  401.     }
  402.  
  403.     // Cleanup Win32 resources
  404.     RegCloseKey( hDPlaySampleRegKey );
  405.     CloseHandle( g_hShutdownEvent );
  406.     CloseHandle( g_hDPDataAvailEvent );
  407.     DeleteCriticalSection( &g_csPlayerList );
  408.     DeleteCriticalSection( &g_csDataList );
  409.     DeleteCriticalSection( &g_csPlayerContext );
  410.     CoUninitialize();
  411.  
  412.     return TRUE;
  413. }
  414.  
  415.  
  416.  
  417.  
  418. //-----------------------------------------------------------------------------
  419. // Name: InitDirectPlay()
  420. // Desc: 
  421. //-----------------------------------------------------------------------------
  422. HRESULT InitDirectPlay()
  423. {
  424.     DPNHANDLE hLobbyLaunchedConnection = NULL;
  425.     HRESULT hr;
  426.  
  427.     // Create IDirectPlay8Peer
  428.     if( FAILED( hr = CoCreateInstance( CLSID_DirectPlay8Peer, NULL, 
  429.                                        CLSCTX_INPROC_SERVER,
  430.                                        IID_IDirectPlay8Peer, 
  431.                                        (LPVOID*) &g_pDP ) ) )
  432.         return DXTRACE_ERR( TEXT("CoCreateInstance"), hr );
  433.  
  434.     // Create IDirectPlay8LobbiedApplication
  435.     if( FAILED( hr = CoCreateInstance( CLSID_DirectPlay8LobbiedApplication, NULL, 
  436.                                        CLSCTX_INPROC_SERVER,
  437.                                        IID_IDirectPlay8LobbiedApplication, 
  438.                                        (LPVOID*) &g_pLobbiedApp ) ) )
  439.         return DXTRACE_ERR( TEXT("CoCreateInstance"), hr );
  440.  
  441.     // Init the helper class, now that g_pDP and g_pLobbiedApp are valid
  442.     g_pNetConnectWizard->Init( g_pDP, g_pLobbiedApp );
  443.  
  444.     // Init IDirectPlay8Peer
  445.     if( FAILED( hr = g_pDP->Initialize( NULL, DirectPlayMessageHandler, 0 ) ) )
  446.         return DXTRACE_ERR( TEXT("Initialize"), hr );
  447.  
  448.     // Init IDirectPlay8LobbiedApplication.  Before this Initialize() returns 
  449.     // a DPL_MSGID_CONNECT msg may come in to the DirectPlayLobbyMessageHandler 
  450.     // so be prepared ahead of time.
  451.     if( FAILED( hr = g_pLobbiedApp->Initialize( NULL, DirectPlayLobbyMessageHandler, 
  452.                                                 &hLobbyLaunchedConnection, 0 ) ) )
  453.         return DXTRACE_ERR( TEXT("Initialize"), hr );
  454.  
  455.     // IDirectPlay8LobbiedApplication::Initialize returns a handle to a connnection
  456.     // if we have been lobby launced.  Initialize is guanteeded to return after 
  457.     // the DPL_MSGID_CONNECT msg has been processed.  So unless a we are expected 
  458.     // multiple lobby connections, we do not need to remember the lobby connection
  459.     // handle since it will be recorded upon the DPL_MSGID_CONNECT msg.
  460.     g_bWasLobbyLaunched = ( hLobbyLaunchedConnection != NULL );
  461.  
  462.     return S_OK;
  463. }
  464.  
  465.  
  466.  
  467.  
  468. //-----------------------------------------------------------------------------
  469. // Name: SampleDlgProc()
  470. // Desc: Handles dialog messages
  471. //-----------------------------------------------------------------------------
  472. INT_PTR CALLBACK SampleDlgProc( HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam )
  473. {
  474.     switch( msg ) 
  475.     {
  476.         case WM_INITDIALOG:
  477.         {
  478.             g_hDlg = hDlg;
  479.             if( FAILED( g_hrDialog = OnInitDialog( hDlg ) ) )
  480.             {
  481.                 DXTRACE_ERR( TEXT("OnInitDialog"), g_hrDialog );
  482.                 EndDialog( hDlg, 0 );
  483.             }
  484.  
  485.             break;
  486.         }
  487.  
  488.         case WM_APP_UPDATE_TARGETS:
  489.         {
  490.             // Display the player names in the UI
  491.             CheckDlgButton( hDlg, IDC_SEND_READY, (g_bSendingData) ? BST_CHECKED : BST_UNCHECKED );
  492.             PostMessage( hDlg, WM_COMMAND, IDC_SEND_READY, 0 );
  493.             FillTargetCombo( g_hDlg );
  494.             ReadCombos( g_hDlg );
  495.             break;
  496.         }
  497.  
  498.         case WM_APP_APPEND_TEXT:
  499.         {
  500.             // Append a string to the edit control
  501.             TCHAR* strNewLogLine = (TCHAR*) wParam;
  502.             AppendTextToEditControl( g_hDlg, strNewLogLine );
  503.             SAFE_DELETE( strNewLogLine );
  504.             break;
  505.         }
  506.  
  507.         case WM_COMMAND:
  508.         {
  509.             switch( LOWORD(wParam) )
  510.             {
  511.                 case IDC_SEND_READY:
  512.                     g_bSendingData = (IsDlgButtonChecked( hDlg, IDC_SEND_READY ) == BST_CHECKED );
  513.                     if( g_bSendingData )
  514.                     {
  515.                         SetTimer( g_hDlg, TIMERID_NETWORK, g_dwTimeBetweenSends, NULL );
  516.                         SetDlgItemText( hDlg, IDC_SEND_READY, TEXT("Sending...") );
  517.                     }
  518.                     else
  519.                     {
  520.                         KillTimer( g_hDlg, TIMERID_NETWORK );
  521.                         SetDlgItemText( hDlg, IDC_SEND_READY, TEXT("Push to Send") );
  522.                     }
  523.                     break;
  524.  
  525.                 case IDCANCEL:
  526.                     g_hrDialog = S_OK;
  527.                     EndDialog( hDlg, 0 );
  528.                     return TRUE;
  529.  
  530.                 case IDC_SEND_SIZE_COMBO:
  531.                 case IDC_SEND_RATE_COMBO:
  532.                 case IDC_SEND_TARGET_COMBO:
  533.                 case IDC_CONNINFO_COMBO:
  534.                     if( HIWORD(wParam) == CBN_SELENDOK )
  535.                         ReadCombos( hDlg );
  536.                     break;
  537.             }
  538.             break;
  539.         }
  540.  
  541.         case WM_TIMER:
  542.         {
  543.             switch( wParam )
  544.             {
  545.                 case TIMERID_NETWORK:
  546.                 {
  547.                     // Send network data
  548.                     if( FAILED( g_hrDialog = SendNetworkData() ) )
  549.                     {
  550.                         DXTRACE_ERR( TEXT("SendNetworkData"), g_hrDialog );
  551.                         EndDialog( hDlg, 0 );
  552.                     }
  553.                     break;
  554.                 }
  555.                 
  556.                 case TIMERID_STATS:
  557.                 {
  558.                     UpdateStats( hDlg );
  559.                     break;
  560.                 }
  561.             }
  562.             break;
  563.         }
  564.     }
  565.  
  566.     return FALSE; // Didn't handle message
  567. }
  568.  
  569.  
  570.  
  571.  
  572. //-----------------------------------------------------------------------------
  573. // Name: OnInitDialog()
  574. // Desc: Inits the dialog.
  575. //-----------------------------------------------------------------------------
  576. HRESULT OnInitDialog( HWND hDlg )
  577. {
  578.     // Load and set the icon
  579.     HICON hIcon = LoadIcon( g_hInst, MAKEINTRESOURCE( IDI_MAIN ) );
  580.     SendMessage( hDlg, WM_SETICON, ICON_BIG,   (LPARAM) hIcon );  // Set big icon
  581.     SendMessage( hDlg, WM_SETICON, ICON_SMALL, (LPARAM) hIcon );  // Set small icon
  582.  
  583.     // Display local player's name
  584.     SetDlgItemText( hDlg, IDC_PLAYER_NAME, g_strLocalPlayerName );
  585.  
  586.     // Start a thread to process the network data
  587.     g_hProcessNetDataThread = (HANDLE) _beginthreadex( NULL, 0, ProcessNetDataProc, 
  588.                                                        hDlg, 0, &g_dwProcessNetDataThreadID );
  589.  
  590.     // Update the dialog box
  591.     SendMessage( hDlg, WM_APP_UPDATE_TARGETS, 0, 0 );
  592.     FillOneTimeCombos( hDlg );
  593.     ReadCombos( hDlg );
  594.  
  595.     // Update the stats every second
  596.     SetTimer( hDlg, TIMERID_STATS, 1000, NULL );
  597.  
  598.     if( g_pNetConnectWizard->IsHostPlayer() )
  599.         SetWindowText( hDlg, TEXT("DataRelay (Host)") );
  600.     else
  601.         SetWindowText( hDlg, TEXT("DataRelay") );
  602.  
  603.     return S_OK;
  604. }
  605.  
  606.  
  607.  
  608.  
  609. //-----------------------------------------------------------------------------
  610. // Name: FillTargetCombo()
  611. // Desc: Fills the target combo with all of the players
  612. //-----------------------------------------------------------------------------
  613. VOID FillTargetCombo( HWND hDlg )
  614. {
  615.     DWORD    dwNumberOfActivePlayers = 0;
  616.     int      nIndex;
  617.     int      nCurSelect;
  618.     int      nNewSelect = 0;
  619.     LONG_PTR lCurItemData; 
  620.     HWND     hTargetCombo;
  621.     HWND     hInfoTargetCombo;
  622.  
  623.     if( hDlg == NULL )
  624.         return;
  625.  
  626.     hTargetCombo = GetDlgItem( hDlg, IDC_SEND_TARGET_COMBO );
  627.     if( hTargetCombo == NULL )
  628.         return;
  629.  
  630.     nCurSelect   = (int)SendMessage( hTargetCombo, CB_GETCURSEL, 0, 0 );
  631.     lCurItemData = (LONG_PTR)SendMessage( hTargetCombo, CB_GETITEMDATA, nCurSelect, 0 );
  632.  
  633.     // Clear combo box
  634.     SendMessage( hTargetCombo, CB_RESETCONTENT, 0, 0 );
  635.  
  636.     // Add "everyone"
  637.     nIndex = (int)SendMessage( hTargetCombo, CB_ADDSTRING, 0, (LPARAM) TEXT("Everyone") );
  638.     SendMessage( hTargetCombo, CB_SETITEMDATA, nIndex, (LPARAM) &g_PlayerHead );
  639.  
  640.     // Enter the player list CS, because we are about to read shared memory, and 
  641.     // the data may be changed concurrently from the DirectPlay
  642.     // message handler threads
  643.     EnterCriticalSection( &g_csPlayerList );
  644.  
  645.     // Add each player
  646.     APP_PLAYER_INFO* pNode = g_PlayerHead.pNext;
  647.     while( pNode != &g_PlayerHead ) 
  648.     {
  649.         if( (pNode->dwFlags & DPNPLAYER_LOCAL) == 0 )
  650.         {
  651.             nIndex = (int)SendMessage( hTargetCombo, CB_ADDSTRING, 0, (LPARAM) pNode->strPlayerName );
  652.             SendMessage( hTargetCombo, CB_SETITEMDATA, nIndex, (LPARAM) pNode );
  653.             if( lCurItemData == (LONG_PTR) pNode )
  654.                 nNewSelect = nIndex;
  655.         }
  656.  
  657.         dwNumberOfActivePlayers++;
  658.         pNode = pNode->pNext;
  659.     }
  660.  
  661.     LeaveCriticalSection( &g_csPlayerList );
  662.      
  663.     SendMessage( hTargetCombo, CB_SETCURSEL, nNewSelect, 0 );  
  664.  
  665.     // Update the number of players in game counter
  666.     TCHAR strNumberPlayers[32];
  667.     wsprintf( strNumberPlayers, TEXT("%d"), dwNumberOfActivePlayers );
  668.     SetDlgItemText( hDlg, IDC_NUM_PLAYERS, strNumberPlayers );
  669.  
  670.     if( dwNumberOfActivePlayers > 1 )
  671.     {
  672.         EnableWindow( GetDlgItem( hDlg, IDC_SEND_READY ), TRUE );
  673.     }
  674.     else
  675.     {
  676.         EnableWindow( GetDlgItem( hDlg, IDC_SEND_READY ), FALSE );
  677.         CheckDlgButton( hDlg, IDC_SEND_READY, BST_UNCHECKED );
  678.         SetDlgItemText( hDlg, IDC_SEND_READY, TEXT("Push to Send") );
  679.     }
  680.  
  681.     // Populate the connection info box    
  682.     hInfoTargetCombo = GetDlgItem( hDlg, IDC_CONNINFO_COMBO );
  683.         
  684.     nCurSelect   = (int)SendMessage( hInfoTargetCombo, CB_GETCURSEL, 0, 0 );
  685.     lCurItemData = (LONG_PTR)SendMessage( hInfoTargetCombo, CB_GETITEMDATA, nCurSelect, 0 );
  686.     
  687.     // Clear combo box
  688.     SendMessage( hInfoTargetCombo, CB_RESETCONTENT, 0, 0 );
  689.     
  690.     // Add "none"
  691.     nIndex = (int)SendMessage( hInfoTargetCombo, CB_ADDSTRING, 0, (LPARAM) TEXT("None") );
  692.     SendMessage( hInfoTargetCombo, CB_SETITEMDATA, nIndex, (LPARAM) &g_PlayerHead );
  693.  
  694.     // Enter the player list CS, because we are about to read shared memory, and 
  695.     // the data may be changed concurrently from the DirectPlay
  696.     // message handler threads
  697.     EnterCriticalSection( &g_csPlayerList );
  698.  
  699.     // Add each player    
  700.     pNode = g_PlayerHead.pNext;
  701.     nNewSelect = 0;
  702.     while( pNode != &g_PlayerHead ) 
  703.     {
  704.         if( (pNode->dwFlags & DPNPLAYER_LOCAL) == 0 )
  705.         {
  706.             nIndex = (int)SendMessage( hInfoTargetCombo, CB_ADDSTRING, 0, (LPARAM) pNode->strPlayerName );
  707.             SendMessage( hInfoTargetCombo, CB_SETITEMDATA, nIndex, (LPARAM) pNode );
  708.             if( lCurItemData == (LONG_PTR) pNode )
  709.                 nNewSelect = nIndex;
  710.         }
  711.  
  712.         dwNumberOfActivePlayers++;
  713.         pNode = pNode->pNext;
  714.     }
  715.  
  716.     LeaveCriticalSection( &g_csPlayerList );
  717.      
  718.     SendMessage( hInfoTargetCombo, CB_SETCURSEL, nNewSelect, 0 );  
  719. }
  720.  
  721.  
  722.  
  723.  
  724. //-----------------------------------------------------------------------------
  725. // Name: FillOneTimeCombos()
  726. // Desc: Fill the unchanging UI combos box
  727. //-----------------------------------------------------------------------------
  728. VOID FillOneTimeCombos( HWND hDlg )
  729. {
  730.     if( hDlg == NULL )
  731.         return;
  732.  
  733.     HWND hRateCombo = GetDlgItem( hDlg, IDC_SEND_RATE_COMBO );
  734.     SendMessage( hRateCombo, WM_SETREDRAW, FALSE, 0 );
  735.     SendMessage( hRateCombo, CB_RESETCONTENT, 0, 0 );
  736.     SendMessage( hRateCombo, CB_ADDSTRING, 0, (LPARAM) TEXT("1000") );
  737.     SendMessage( hRateCombo, CB_ADDSTRING, 0, (LPARAM) TEXT("500") );
  738.     SendMessage( hRateCombo, CB_ADDSTRING, 0, (LPARAM) TEXT("250") );
  739.     SendMessage( hRateCombo, CB_ADDSTRING, 0, (LPARAM) TEXT("100") );
  740.     SendMessage( hRateCombo, CB_ADDSTRING, 0, (LPARAM) TEXT("50") );
  741.     SendMessage( hRateCombo, CB_ADDSTRING, 0, (LPARAM) TEXT("0") );
  742.     SendMessage( hRateCombo, WM_SETREDRAW, TRUE, 0 );
  743.     SendMessage( hRateCombo, CB_SETCURSEL, 0, 0 );  
  744.     
  745.     HWND hSizeCombo = GetDlgItem( hDlg, IDC_SEND_SIZE_COMBO );
  746.     SendMessage( hSizeCombo, WM_SETREDRAW, FALSE, 0 );
  747.     SendMessage( hSizeCombo, CB_RESETCONTENT, 0, 0 );
  748.     SendMessage( hSizeCombo, CB_ADDSTRING, 0, (LPARAM) TEXT("512") );
  749.     SendMessage( hSizeCombo, CB_ADDSTRING, 0, (LPARAM) TEXT("256") );
  750.     SendMessage( hSizeCombo, CB_ADDSTRING, 0, (LPARAM) TEXT("128") );
  751.     SendMessage( hSizeCombo, CB_ADDSTRING, 0, (LPARAM) TEXT("64") );
  752.     SendMessage( hSizeCombo, CB_ADDSTRING, 0, (LPARAM) TEXT("32") );
  753.     SendMessage( hSizeCombo, CB_ADDSTRING, 0, (LPARAM) TEXT("16") );
  754.     SendMessage( hSizeCombo, WM_SETREDRAW, TRUE, 0 );
  755.     SendMessage( hSizeCombo, CB_SETCURSEL, 0, 0 );  
  756.  
  757.     HWND hTimeoutCombo = GetDlgItem( hDlg, IDC_SEND_TIMEOUT_COMBO );
  758.     SendMessage( hTimeoutCombo, WM_SETREDRAW, FALSE, 0 );
  759.     SendMessage( hTimeoutCombo, CB_RESETCONTENT, 0, 0 );
  760.     SendMessage( hTimeoutCombo, CB_ADDSTRING, 0, (LPARAM) TEXT("5") );
  761.     SendMessage( hTimeoutCombo, CB_ADDSTRING, 0, (LPARAM) TEXT("10") );
  762.     SendMessage( hTimeoutCombo, CB_ADDSTRING, 0, (LPARAM) TEXT("20") );
  763.     SendMessage( hTimeoutCombo, CB_ADDSTRING, 0, (LPARAM) TEXT("50") );
  764.     SendMessage( hTimeoutCombo, CB_ADDSTRING, 0, (LPARAM) TEXT("100") );
  765.     SendMessage( hTimeoutCombo, CB_ADDSTRING, 0, (LPARAM) TEXT("250") );
  766.     SendMessage( hTimeoutCombo, CB_ADDSTRING, 0, (LPARAM) TEXT("500") );
  767.     SendMessage( hTimeoutCombo, WM_SETREDRAW, TRUE, 0 );
  768.     SendMessage( hTimeoutCombo, CB_SETCURSEL, 2, 0 );  
  769. }
  770.  
  771.  
  772.  
  773.  
  774. //-----------------------------------------------------------------------------
  775. // Name: ReadCombos()
  776. // Desc: Reads the state of the combos and updates the global vars
  777. //-----------------------------------------------------------------------------
  778. VOID ReadCombos( HWND hDlg )
  779. {
  780.     TCHAR strText[128];
  781.     int nCurSelect;
  782.  
  783.     // Get target player
  784.     HWND hTargetCombo  = GetDlgItem( hDlg, IDC_SEND_TARGET_COMBO );
  785.     nCurSelect = (int)SendMessage( hTargetCombo, CB_GETCURSEL, 0, 0 );
  786.     if( nCurSelect != CB_ERR ) 
  787.     {
  788.         g_pTargetPlayer = (APP_PLAYER_INFO*) SendMessage( hTargetCombo, CB_GETITEMDATA, 
  789.                                                           nCurSelect, 0 );
  790.     }
  791.  
  792.     // Get rate (in ms)
  793.     HWND hRateCombo  = GetDlgItem( hDlg, IDC_SEND_RATE_COMBO );
  794.     nCurSelect = (int)SendMessage( hRateCombo, CB_GETCURSEL, 0, 0 );
  795.     if( nCurSelect != CB_ERR ) 
  796.     {
  797.         SendMessage( hRateCombo, CB_GETLBTEXT, nCurSelect, (LPARAM) strText );
  798.         g_dwTimeBetweenSends = _ttoi( strText );
  799.         KillTimer( g_hDlg, TIMERID_NETWORK );
  800.         SetTimer( g_hDlg, TIMERID_NETWORK, g_dwTimeBetweenSends, NULL );
  801.     }
  802.  
  803.     // Get size (in bytes)
  804.     HWND hSizeCombo = GetDlgItem( hDlg, IDC_SEND_SIZE_COMBO );
  805.     nCurSelect = (int)SendMessage( hSizeCombo, CB_GETCURSEL, 0, 0 );
  806.     if( nCurSelect != CB_ERR ) 
  807.     {
  808.         SendMessage( hSizeCombo, CB_GETLBTEXT, nCurSelect, (LPARAM) strText );
  809.         g_dwSendSize = _ttoi( strText );
  810.     }
  811.  
  812.     // Get timeout (in ms)
  813.     HWND hTimeoutCombo = GetDlgItem( hDlg, IDC_SEND_TIMEOUT_COMBO );
  814.     nCurSelect = (int)SendMessage( hTimeoutCombo, CB_GETCURSEL, 0, 0 );
  815.     if( nCurSelect != CB_ERR ) 
  816.     {
  817.         SendMessage( hTimeoutCombo, CB_GETLBTEXT, nCurSelect, (LPARAM) strText );
  818.         g_dwSendTimeout = _ttoi( strText );
  819.     }
  820.  
  821.     // Get the ConnectionInfo Target player
  822.     HWND hConnTargetCombo  = GetDlgItem( hDlg, IDC_CONNINFO_COMBO );
  823.     nCurSelect = (int)SendMessage( hConnTargetCombo, CB_GETCURSEL, 0, 0 );
  824.     if( nCurSelect != CB_ERR ) 
  825.     {
  826.         g_pConnInfoTargetPlayer = (APP_PLAYER_INFO*) SendMessage( hConnTargetCombo, CB_GETITEMDATA, 
  827.                                                       nCurSelect, 0 );
  828.     }
  829. }
  830.  
  831.  
  832.  
  833.  
  834. //-----------------------------------------------------------------------------
  835. // Name: DirectPlayMessageHandler
  836. // Desc: Handler for DirectPlay messages.  This function is called by
  837. //       the DirectPlay message handler pool of threads, so be care of thread
  838. //       synchronization problems with shared memory
  839. //-----------------------------------------------------------------------------
  840. HRESULT WINAPI DirectPlayMessageHandler( PVOID pvUserContext, 
  841.                                          DWORD dwMessageId, 
  842.                                          PVOID pMsgBuffer )
  843. {
  844.     // Try not to stay in this message handler for too long, otherwise
  845.     // there will be a backlog of data.  The best solution is to 
  846.     // queue data as it comes in, and then handle it on other threads
  847.     // as this sample shows
  848.  
  849.     // This function is called by the DirectPlay message handler pool of 
  850.     // threads, so be care of thread synchronization problems with shared memory
  851.  
  852.     HRESULT hReturn = S_OK;
  853.  
  854.     switch( dwMessageId )
  855.     {
  856.         case DPN_MSGID_CREATE_PLAYER:
  857.         {
  858.             HRESULT hr;
  859.             PDPNMSG_CREATE_PLAYER pCreatePlayerMsg;
  860.             pCreatePlayerMsg = (PDPNMSG_CREATE_PLAYER) pMsgBuffer;
  861.  
  862.             APP_PLAYER_INFO* pPlayerInfo;
  863.             if( FAILED( hr = LinkPlayer( pCreatePlayerMsg->dpnidPlayer, 
  864.                                          &pPlayerInfo ) ) )
  865.                 return DXTRACE_ERR( TEXT("LinkPlayer"), hr );
  866.  
  867.             // Tell DirectPlay to store this pPlayerInfo 
  868.             // pointer in the pvPlayerContext.
  869.             pCreatePlayerMsg->pvPlayerContext = pPlayerInfo;
  870.  
  871.             // Post a message to the dialog thread to update the 
  872.             // UI.  This keeps the DirectPlay message handler 
  873.             // from blocking
  874.             if( g_hDlg != NULL )
  875.                 PostMessage( g_hDlg, WM_APP_UPDATE_TARGETS, 0, 0 );
  876.             break;
  877.         }
  878.  
  879.         case DPN_MSGID_DESTROY_PLAYER:
  880.         {
  881.             PDPNMSG_DESTROY_PLAYER pDestroyPlayerMsg;
  882.             pDestroyPlayerMsg = (PDPNMSG_DESTROY_PLAYER)pMsgBuffer;
  883.             APP_PLAYER_INFO* pPlayerInfo = (APP_PLAYER_INFO*) pDestroyPlayerMsg->pvPlayerContext;
  884.  
  885.             // Enter the player list CS, because we are about to 
  886.             // modify shared memory, and the data may be changed 
  887.             // concurrently from other DirectPlay message handler threads 
  888.             EnterCriticalSection( &g_csPlayerList );
  889.  
  890.             // Unlink pPlayerInfo from the chain, m_PlayerHead.
  891.             pPlayerInfo->pPrev->pNext = pPlayerInfo->pNext;
  892.             pPlayerInfo->pNext->pPrev = pPlayerInfo->pPrev;
  893.  
  894.             LeaveCriticalSection( &g_csPlayerList );
  895.  
  896.             // Stop sending if the target was destroyed
  897.             if( g_pTargetPlayer )
  898.             {
  899.                 if( g_pTargetPlayer->dpnidPlayer == pPlayerInfo->dpnidPlayer )
  900.                     g_bSendingData = FALSE;
  901.             }
  902.  
  903.             PLAYER_LOCK();                  // enter player context CS
  904.             PLAYER_RELEASE( pPlayerInfo );  // Release player and cleanup if needed
  905.             PLAYER_UNLOCK();                // leave player context CS
  906.  
  907.             // Post a message to the dialog thread to update the 
  908.             // UI.  This keeps the DirectPlay message handler 
  909.             // from blocking
  910.             if( g_hDlg != NULL )
  911.                 PostMessage( g_hDlg, WM_APP_UPDATE_TARGETS, 0, 0 );
  912.             break;
  913.         }
  914.  
  915.         case DPN_MSGID_TERMINATE_SESSION:
  916.         {
  917.             PDPNMSG_TERMINATE_SESSION pTerminateSessionMsg;
  918.             pTerminateSessionMsg = (PDPNMSG_TERMINATE_SESSION)pMsgBuffer;
  919.  
  920.             g_hrDialog = DPNERR_CONNECTIONLOST;
  921.             EndDialog( g_hDlg, 0 );
  922.             break;
  923.         }
  924.  
  925.         case DPN_MSGID_HOST_MIGRATE:
  926.         {
  927.             PDPNMSG_HOST_MIGRATE pHostMigrateMsg;
  928.             pHostMigrateMsg = (PDPNMSG_HOST_MIGRATE)pMsgBuffer;
  929.  
  930.             if( pHostMigrateMsg->dpnidNewHost == g_dpnidLocalPlayer )
  931.                 SetWindowText( g_hDlg, TEXT("DataRelay (Host)") );
  932.             break;
  933.         }
  934.  
  935.         case DPN_MSGID_RECEIVE:
  936.         {
  937.             PDPNMSG_RECEIVE pReceiveMsg;
  938.             pReceiveMsg = (PDPNMSG_RECEIVE)pMsgBuffer;
  939.             APP_PLAYER_INFO* pPlayerInfo = (APP_PLAYER_INFO*) pReceiveMsg->pvPlayerContext;
  940.             if( NULL == pPlayerInfo )
  941.                 break;
  942.  
  943.             GAMEMSG_GENERIC* pMsg = (GAMEMSG_GENERIC*) pReceiveMsg->pReceiveData;
  944.             if( pMsg->dwType == GAME_MSGID_GAMEPACKET )
  945.             {
  946.                 GAMEMSG_GENERIC* pDataMsg = (GAMEMSG_GENERIC*) pMsg;
  947.  
  948.                 // Make a new GAMEMSG_DATA_NODE and hand it off to the
  949.                 // app worker thread.  It will process the node, and
  950.                 // then update the UI to show that the packet was processed
  951.                 GAMEMSG_DATA_NODE* pDataMsgNode = new GAMEMSG_DATA_NODE;                
  952.                 ZeroMemory( pDataMsgNode, sizeof(GAMEMSG_DATA_NODE) ); 
  953.  
  954.                 pDataMsgNode->dwType            = DATA_TYPE_NETPACKET_RECIEVE;
  955.                 pDataMsgNode->dwPacketId        = pDataMsg->dwPacketId;
  956.                 pDataMsgNode->pDataMsg          = pDataMsg;
  957.                 pDataMsgNode->dwReceiveDataSize = pReceiveMsg->dwReceiveDataSize;
  958.                 pDataMsgNode->pPlayerFrom       = pPlayerInfo;
  959.                 pDataMsgNode->hBufferHandle     = pReceiveMsg->hBufferHandle;
  960.  
  961.                 // Enter the data list CS, because we are about to modify shared memory, and 
  962.                 // the data may be changed concurrently from other DirectPlay
  963.                 // message handler threads, as well as the the app worker thread
  964.                 EnterCriticalSection( &g_csDataList );
  965.  
  966.                 g_dwDataRecieved += pReceiveMsg->dwReceiveDataSize;
  967.  
  968.                 // Then add it to the circular linked list, g_DataHead
  969.                 // so it can be processed by a worker thread
  970.                 pDataMsgNode->pNext = &g_DataHead;
  971.                 pDataMsgNode->pPrev = g_DataHead.pPrev;
  972.                 g_DataHead.pPrev->pNext = pDataMsgNode;
  973.                 g_DataHead.pPrev        = pDataMsgNode;
  974.  
  975.                 LeaveCriticalSection( &g_csDataList );
  976.  
  977.                 // Tell the app worker thread that there 
  978.                 // is new network data availible
  979.                 SetEvent( g_hDPDataAvailEvent );
  980.  
  981.                 // Tell DirectPlay to assume that ownership of the buffer 
  982.                 // has been transferred to the application, and so it will 
  983.                 // neither free nor modify it until ownership is returned 
  984.                 // to DirectPlay through the ReturnBuffer() call.
  985.                 hReturn = DPNSUCCESS_PENDING;
  986.             }
  987.             break;
  988.         }
  989.  
  990.         case DPN_MSGID_SEND_COMPLETE:
  991.         {
  992.             PDPNMSG_SEND_COMPLETE pSendCompleteMsg;
  993.             pSendCompleteMsg = (PDPNMSG_SEND_COMPLETE)pMsgBuffer;
  994.  
  995.             GAMEMSG_GENERIC* pGameMsg;
  996.             pGameMsg = (GAMEMSG_GENERIC*) pSendCompleteMsg->pvUserContext;
  997.  
  998.             if( pSendCompleteMsg->hResultCode == DPNERR_TIMEDOUT )
  999.             {
  1000.                 // Make a new GAMEMSG_DATA_NODE and hand it off to the
  1001.                 // app worker thread.  It will process the node, and
  1002.                 // then update the UI to show that the packet timed out
  1003.                 GAMEMSG_DATA_NODE* pDataMsgNode = new GAMEMSG_DATA_NODE;
  1004.                 ZeroMemory( pDataMsgNode, sizeof(GAMEMSG_DATA_NODE) ); 
  1005.                 
  1006.                 pDataMsgNode->dwType     = DATA_TYPE_NETPACKET_TIMEOUT;
  1007.                 pDataMsgNode->dwPacketId = pGameMsg->dwPacketId;
  1008.  
  1009.                 // Enter the data list CS, because we are about to modify shared memory, and 
  1010.                 // the data may be changed concurrently from other DirectPlay
  1011.                 // message handler threads, as well as the the app worker thread
  1012.                 EnterCriticalSection( &g_csDataList );
  1013.  
  1014.                 // Then add it to the circular linked list, g_DataHead
  1015.                 // so it can be processed by a worker thread
  1016.                 pDataMsgNode->pNext = &g_DataHead;
  1017.                 pDataMsgNode->pPrev = g_DataHead.pPrev;
  1018.                 g_DataHead.pPrev->pNext = pDataMsgNode;
  1019.                 g_DataHead.pPrev        = pDataMsgNode;
  1020.  
  1021.                 LeaveCriticalSection( &g_csDataList );
  1022.  
  1023.                 // Tell the app worker thread that there 
  1024.                 // is new data availible. 
  1025.                 SetEvent( g_hDPDataAvailEvent );
  1026.             }
  1027.  
  1028.             SAFE_DELETE( pGameMsg );
  1029.  
  1030.             break;
  1031.         }
  1032.     }
  1033.  
  1034.     // Make sure the DirectPlay MessageHandler calls the CNetConnectWizard handler, 
  1035.     // so it can be informed of messages such as DPN_MSGID_ENUM_HOSTS_RESPONSE.
  1036.     if( DPNSUCCESS_PENDING != hReturn && SUCCEEDED(hReturn) && g_pNetConnectWizard )
  1037.         hReturn = g_pNetConnectWizard->MessageHandler( pvUserContext, dwMessageId, pMsgBuffer );
  1038.     
  1039.     return hReturn;
  1040. }
  1041.  
  1042.  
  1043.  
  1044.  
  1045. //-----------------------------------------------------------------------------
  1046. // Name: LinkPlayer()
  1047. // Desc: Link a new player to the circular linked list, g_PlayerHead.  This
  1048. //       is called by the DirectPlay message handler threads, so we enter a 
  1049. //       CS to avoid thread syncoronization problems.
  1050. //-----------------------------------------------------------------------------
  1051. HRESULT LinkPlayer( DPNID dpnid, APP_PLAYER_INFO** ppPlayerInfo )
  1052. {
  1053.     HRESULT hr;
  1054.  
  1055.     // Create a new and fill in a APP_PLAYER_INFO
  1056.     APP_PLAYER_INFO* pPlayerInfo = new APP_PLAYER_INFO;
  1057.     pPlayerInfo->dpnidPlayer = dpnid;
  1058.     pPlayerInfo->lRefCount   = 1;
  1059.  
  1060.     // Get the peer info and extract its name
  1061.     DWORD dwSize = 0;
  1062.     DPN_PLAYER_INFO* pdpPlayerInfo = NULL;
  1063.     hr = DPNERR_CONNECTING;
  1064.     
  1065.     // GetPeerInfo might return DPNERR_CONNECTING when connecting, 
  1066.     // so just keep calling it if it does
  1067.     while( hr == DPNERR_CONNECTING ) 
  1068.         hr = g_pDP->GetPeerInfo( dpnid, pdpPlayerInfo, &dwSize, 0 );                                
  1069.         
  1070.     if( hr == DPNERR_BUFFERTOOSMALL )
  1071.     {
  1072.         pdpPlayerInfo = (DPN_PLAYER_INFO*) new BYTE[ dwSize ];
  1073.         ZeroMemory( pdpPlayerInfo, dwSize );
  1074.         pdpPlayerInfo->dwSize = sizeof(DPN_PLAYER_INFO);
  1075.         
  1076.         hr = g_pDP->GetPeerInfo( dpnid, pdpPlayerInfo, &dwSize, 0 );
  1077.         if( SUCCEEDED(hr) )
  1078.         {
  1079.             // This stores a extra TCHAR copy of the player name for 
  1080.             // easier access.  This will be redundent copy since DPlay 
  1081.             // also keeps a copy of the player name in GetPeerInfo()
  1082.             DXUtil_ConvertWideStringToGeneric( pPlayerInfo->strPlayerName, 
  1083.                                                pdpPlayerInfo->pwszName, MAX_PLAYER_NAME );    
  1084.                                                
  1085.             pPlayerInfo->dwFlags     = pdpPlayerInfo->dwPlayerFlags;
  1086.             
  1087.             if( pdpPlayerInfo->dwPlayerFlags & DPNPLAYER_LOCAL )
  1088.                 g_dpnidLocalPlayer = dpnid;
  1089.         }
  1090.         
  1091.         SAFE_DELETE_ARRAY( pdpPlayerInfo );
  1092.     }
  1093.  
  1094.     // Enter the player list CS, because we are about to modify shared memory, and 
  1095.     // the data may be changed concurrently from the DirectPlay
  1096.     // message handler threads 
  1097.     EnterCriticalSection( &g_csPlayerList );
  1098.  
  1099.     // Then add it to the circular linked list, g_PlayerHead,
  1100.     pPlayerInfo->pNext = g_PlayerHead.pNext;
  1101.     pPlayerInfo->pPrev = &g_PlayerHead;
  1102.     g_PlayerHead.pNext->pPrev = pPlayerInfo;
  1103.     g_PlayerHead.pNext        = pPlayerInfo;
  1104.  
  1105.     LeaveCriticalSection( &g_csPlayerList );
  1106.  
  1107.     if( ppPlayerInfo )
  1108.         *ppPlayerInfo = pPlayerInfo;
  1109.  
  1110.     return S_OK;
  1111. }
  1112.  
  1113.  
  1114.  
  1115.  
  1116.  
  1117.  
  1118.  
  1119.  
  1120. //-----------------------------------------------------------------------------
  1121. // Name: DirectPlayLobbyMessageHandler
  1122. // Desc: Handler for DirectPlay lobby messages.  This function is called by
  1123. //       the DirectPlay lobby message handler pool of threads, so be careful of 
  1124. //       thread synchronization problems with shared memory
  1125. //-----------------------------------------------------------------------------
  1126. HRESULT WINAPI DirectPlayLobbyMessageHandler( PVOID pvUserContext, 
  1127.                                               DWORD dwMessageId, 
  1128.                                               PVOID pMsgBuffer )
  1129. {
  1130.     switch( dwMessageId )
  1131.     {
  1132.         case DPL_MSGID_CONNECT:
  1133.         {
  1134.             PDPL_MESSAGE_CONNECT pConnectMsg;
  1135.             pConnectMsg = (PDPL_MESSAGE_CONNECT)pMsgBuffer;
  1136.  
  1137.             // The CNetConnectWizard will handle this message for us,
  1138.             // so there is nothing we need to do here for this simple
  1139.             // sample.
  1140.             break;
  1141.         }
  1142.  
  1143.         case DPL_MSGID_DISCONNECT:
  1144.         {
  1145.             PDPL_MESSAGE_DISCONNECT pDisconnectMsg;
  1146.             pDisconnectMsg = (PDPL_MESSAGE_DISCONNECT)pMsgBuffer;
  1147.  
  1148.             // We should free any data associated with the lobby 
  1149.             // client here, but there is none.
  1150.             break;
  1151.         }
  1152.  
  1153.         case DPL_MSGID_RECEIVE:
  1154.         {
  1155.             PDPL_MESSAGE_RECEIVE pReceiveMsg;
  1156.             pReceiveMsg = (PDPL_MESSAGE_RECEIVE)pMsgBuffer;
  1157.  
  1158.             // The lobby client sent us data.  This sample doesn't
  1159.             // expected data from the client, but it is useful 
  1160.             // for more complex apps.
  1161.             break;
  1162.         }
  1163.  
  1164.         case DPL_MSGID_CONNECTION_SETTINGS:
  1165.         {
  1166.             PDPL_MESSAGE_CONNECTION_SETTINGS pConnectionStatusMsg;
  1167.             pConnectionStatusMsg = (PDPL_MESSAGE_CONNECTION_SETTINGS)pMsgBuffer;
  1168.  
  1169.             // The lobby client has changed the connection settings.  
  1170.             // This simple sample doesn't handle this, but more complex apps may
  1171.             // want to.
  1172.             break;
  1173.         }
  1174.     }
  1175.  
  1176.     // Make sure the DirectPlay MessageHandler calls the CNetConnectWizard handler, 
  1177.     // so the wizard can be informed of lobby messages such as DPL_MSGID_CONNECT
  1178.     if( g_pNetConnectWizard )
  1179.         return g_pNetConnectWizard->LobbyMessageHandler( pvUserContext, dwMessageId, 
  1180.                                                          pMsgBuffer );
  1181.     
  1182.     return S_OK;
  1183. }
  1184.  
  1185.  
  1186.  
  1187.  
  1188. //-----------------------------------------------------------------------------
  1189. // Name: SendNetworkData()
  1190. // Desc: Sends a network packet of the specified size to the specified target.
  1191. //       This is called by the dialog UI thread.
  1192. //-----------------------------------------------------------------------------
  1193. HRESULT SendNetworkData()
  1194. {
  1195.     HRESULT hr;    
  1196.  
  1197.     if( !g_bSendingData || 
  1198.         IsDlgButtonChecked( g_hDlg, IDC_SEND_READY ) == BST_UNCHECKED )
  1199.     {
  1200.         KillTimer( g_hDlg, TIMERID_NETWORK );
  1201.         return S_OK;
  1202.     }
  1203.  
  1204.     if( g_pTargetPlayer == NULL )
  1205.         return E_FAIL;
  1206.  
  1207.     GAMEMSG_GENERIC* pGameMsg = NULL;
  1208.     DWORD            dwBufferSize;
  1209.  
  1210.     // Create a game packet based on the selected choices
  1211.     switch( g_dwSendSize )
  1212.     {
  1213.         case 512:
  1214.         {
  1215.             GAMEMSG_DATA_512* pMsg512 = new GAMEMSG_DATA_512;
  1216.             FillMemory( pMsg512->pBuffer, sizeof(pMsg512->pBuffer), 1 );
  1217.             pGameMsg = pMsg512;
  1218.             dwBufferSize = sizeof(GAMEMSG_DATA_512);
  1219.             break;
  1220.         }
  1221.         
  1222.         case 256:
  1223.         {
  1224.             GAMEMSG_DATA_256* pMsg256 = new GAMEMSG_DATA_256;
  1225.             FillMemory( pMsg256->pBuffer, sizeof(pMsg256->pBuffer), 2 );
  1226.             pGameMsg = pMsg256;
  1227.             dwBufferSize = sizeof(GAMEMSG_DATA_256);
  1228.             break;
  1229.         }
  1230.  
  1231.         case 128:
  1232.         {
  1233.             GAMEMSG_DATA_128* pMsg128 = new GAMEMSG_DATA_128;
  1234.             FillMemory( pMsg128->pBuffer, sizeof(pMsg128->pBuffer), 3 );
  1235.             pGameMsg = pMsg128;
  1236.             dwBufferSize = sizeof(GAMEMSG_DATA_128);
  1237.             break;
  1238.         }
  1239.  
  1240.         case 64:
  1241.         {
  1242.             GAMEMSG_DATA_64* pMsg64 = new GAMEMSG_DATA_64;
  1243.             FillMemory( pMsg64->pBuffer, sizeof(pMsg64->pBuffer), 4 );
  1244.             pGameMsg = pMsg64;
  1245.             dwBufferSize = sizeof(GAMEMSG_DATA_64);
  1246.             break;
  1247.         }
  1248.  
  1249.         case 32:
  1250.         {
  1251.             GAMEMSG_DATA_32* pMsg32 = new GAMEMSG_DATA_32;
  1252.             FillMemory( pMsg32->pBuffer, sizeof(pMsg32->pBuffer), 5 );
  1253.             pGameMsg = pMsg32;
  1254.             dwBufferSize = sizeof(GAMEMSG_DATA_32);
  1255.             break;
  1256.         }
  1257.  
  1258.         default:
  1259.         case 16:
  1260.         {
  1261.             GAMEMSG_DATA_16* pMsg16 = new GAMEMSG_DATA_16;
  1262.             FillMemory( pMsg16->pBuffer, sizeof(pMsg16->pBuffer), 6 );
  1263.             pGameMsg = pMsg16;
  1264.             dwBufferSize = sizeof(GAMEMSG_DATA_16);
  1265.             break;
  1266.         }
  1267.     }
  1268.  
  1269.     // Update the rest of the game msg 
  1270.     g_dwPacketId++;
  1271.     pGameMsg->dwPacketId   = g_dwPacketId;
  1272.     pGameMsg->dwType       = GAME_MSGID_GAMEPACKET;
  1273.             
  1274.     // Make a new GAMEMSG_DATA_NODE and hand it off to the
  1275.     // app worker thread.  It will process the node, and
  1276.     // then update the UI to show that a packet was sent
  1277.     GAMEMSG_DATA_NODE* pDataMsgNode = new GAMEMSG_DATA_NODE;
  1278.     ZeroMemory( pDataMsgNode, sizeof(GAMEMSG_DATA_NODE) ); 
  1279.  
  1280.     pDataMsgNode->dwType            = DATA_TYPE_NETPACKET_SENT;
  1281.     pDataMsgNode->dwPacketId        = pGameMsg->dwPacketId;
  1282.     pDataMsgNode->dwReceiveDataSize = dwBufferSize;
  1283.  
  1284.     // Enter the data list CS, because we are about to modify shared memory, and 
  1285.     // the data may be changed concurrently from other DirectPlay
  1286.     // message handler threads, as well as the the app worker thread
  1287.     EnterCriticalSection( &g_csDataList );
  1288.  
  1289.     // Then add it to the circular linked list, g_DataHead
  1290.     // so it can be processed by a worker thread
  1291.     pDataMsgNode->pNext = &g_DataHead;
  1292.     pDataMsgNode->pPrev = g_DataHead.pPrev;
  1293.     g_DataHead.pPrev->pNext = pDataMsgNode;
  1294.     g_DataHead.pPrev        = pDataMsgNode;
  1295.  
  1296.     LeaveCriticalSection( &g_csDataList );
  1297.  
  1298.     // This var is only accessed by the dialog 
  1299.     // thread, so it is safe to access it directly
  1300.     g_dwDataSent += dwBufferSize;
  1301.  
  1302.     DPN_BUFFER_DESC bufferDesc;
  1303.     bufferDesc.dwBufferSize = dwBufferSize;
  1304.     bufferDesc.pBufferData  = (BYTE*) pGameMsg;
  1305.  
  1306.     DPNHANDLE hAsync;
  1307.     hr = g_pDP->SendTo( g_pTargetPlayer->dpnidPlayer, &bufferDesc, 1,
  1308.                         g_dwSendTimeout, pGameMsg, &hAsync, 
  1309.                         DPNSEND_NOLOOPBACK | DPNSEND_NOCOPY );
  1310.     // Ignore all errors except DPNERR_INVALIDPLAYER
  1311.     if( hr == DPNERR_INVALIDPLAYER )
  1312.     {
  1313.         // Stop sending if the target left game
  1314.         g_bSendingData = FALSE;
  1315.         PostMessage( g_hDlg, WM_APP_UPDATE_TARGETS, 0, 0 );
  1316.         return S_OK;
  1317.     }
  1318.     
  1319.  
  1320.     // Tell the app worker thread that there 
  1321.     // is new data availible. 
  1322.     SetEvent( g_hDPDataAvailEvent );
  1323.  
  1324.     return S_OK;
  1325. }
  1326.  
  1327.  
  1328.  
  1329.  
  1330. //-----------------------------------------------------------------------------
  1331. // Name: ProcessNetDataProc()
  1332. // Desc: Worker thread that processes data found in g_DataHead, 
  1333. //       and updates the UI state based on the data 
  1334. //-----------------------------------------------------------------------------
  1335. UINT WINAPI ProcessNetDataProc( LPVOID lpParameter )
  1336. {
  1337.     HWND   hDlg = (HWND) lpParameter;
  1338.     DWORD  dwResult;
  1339.     BOOL   bDone = FALSE;
  1340.     HANDLE ahHandles[2];
  1341.  
  1342.     ahHandles[0] = g_hDPDataAvailEvent;
  1343.     ahHandles[1] = g_hShutdownEvent;
  1344.  
  1345.     while( !bDone ) 
  1346.     { 
  1347.         dwResult = WaitForMultipleObjects( 2, ahHandles, FALSE, INFINITE );
  1348.  
  1349.         switch( dwResult )
  1350.         {
  1351.             case WAIT_OBJECT_0 + 0:
  1352.             {
  1353.                 // g_hDPDataAvailEvent is signaled, so there is 
  1354.                 // network data available to process
  1355.                 if( FAILED( g_hrDialog = ProcessData() ) ) 
  1356.                 {
  1357.                     DXTRACE_ERR( TEXT("ProcessData"), g_hrDialog );
  1358.                     bDone = TRUE;
  1359.                 }
  1360.                 break;
  1361.             }
  1362.  
  1363.             case WAIT_OBJECT_0 + 1:
  1364.             {
  1365.                 // g_hShutdownEvent is signaled, so shut down
  1366.                 bDone = TRUE;
  1367.                 break;
  1368.             }
  1369.         }
  1370.     }
  1371.  
  1372.     return 0;
  1373. }   
  1374.  
  1375.  
  1376.  
  1377.  
  1378. //-----------------------------------------------------------------------------
  1379. // Name: ProcessData()
  1380. // Desc: This is called by the app worker thread to process the data found in
  1381. //       g_DataHead.  The DirectPlay message handler threads add new nodes 
  1382. //       upon events such as packet packet recieve/send or timeout.
  1383. //-----------------------------------------------------------------------------
  1384. HRESULT ProcessData()
  1385. {
  1386.     TCHAR* strNewLogLine = NULL;
  1387.  
  1388.     // Enter the data list CS, because we are about to read shared memory, and 
  1389.     // the data may be changed concurrently from the DirectPlay
  1390.     // message handler threads 
  1391.     EnterCriticalSection( &g_csDataList );
  1392.  
  1393.     GAMEMSG_DATA_NODE* pDeleteNode; 
  1394.     GAMEMSG_DATA_NODE* pNode = g_DataHead.pNext;
  1395.     while( pNode != &g_DataHead )
  1396.     {
  1397.         // Allocate a string on the heap, and fill it
  1398.         // with text about what happened.
  1399.         strNewLogLine = new TCHAR[MAX_PATH];
  1400.  
  1401.         switch( pNode->dwType )
  1402.         {
  1403.             case DATA_TYPE_NETPACKET_RECIEVE:
  1404.                 wsprintf( strNewLogLine, TEXT("Got %s's #%d (%d bytes)\r\n"), 
  1405.                         pNode->pPlayerFrom->strPlayerName,
  1406.                         pNode->dwPacketId,            
  1407.                         pNode->dwReceiveDataSize );           
  1408.  
  1409.                 // The app can do more process of the network 
  1410.                 // packet here here -- i.e. change the
  1411.                 // state of the game.  This simple sample just
  1412.                 // updates the UI.
  1413.                 break;
  1414.  
  1415.             case DATA_TYPE_NETPACKET_SENT:
  1416.                 wsprintf( strNewLogLine, TEXT("Sending #%d to %s (%d bytes)\r\n"), 
  1417.                                          pNode->dwPacketId,            
  1418.                                          g_pTargetPlayer->strPlayerName,
  1419.                                          pNode->dwReceiveDataSize );           
  1420.                 break;
  1421.  
  1422.             case DATA_TYPE_NETPACKET_TIMEOUT:
  1423.                 wsprintf( strNewLogLine, TEXT("Packet #%d timed out\r\n"), 
  1424.                          pNode->dwPacketId );           
  1425.                 break;
  1426.         }
  1427.  
  1428.         // Post a message to the dialog thread, so it can
  1429.         // take the string on the heap, and appead it to the log.
  1430.         // If the log is appended from this thread, then if the dialog
  1431.         // thread tries to enter the g_csDataList CS, then it will be
  1432.         // locked until this thread release it, and the 
  1433.         // AppendTextToEditControl function relies on reponses from the 
  1434.         // dialog thread, so a race condition is possible unless
  1435.         // the dialog thread itself appeads the text.
  1436.         PostMessage( g_hDlg, WM_APP_APPEND_TEXT, (WPARAM) strNewLogLine, 0 );
  1437.  
  1438.         // The dialog thread will free the string
  1439.         strNewLogLine = NULL;
  1440.  
  1441.         if( pNode->hBufferHandle != NULL )
  1442.         {
  1443.             // Done with the buffer, so return it DirectPlay, 
  1444.             // so that the memory can be reused
  1445.             g_pDP->ReturnBuffer( pNode->hBufferHandle,0 );
  1446.         }
  1447.  
  1448.         // Unlink pPlayerInfo from the chain, g_DataHead.
  1449.         pNode->pPrev->pNext = pNode->pNext;
  1450.         pNode->pNext->pPrev = pNode->pPrev;
  1451.  
  1452.         pDeleteNode = pNode;
  1453.         pNode = pNode->pNext;
  1454.  
  1455.         assert( pDeleteNode != &g_DataHead );
  1456.         SAFE_DELETE( pDeleteNode );
  1457.     }
  1458.  
  1459.     LeaveCriticalSection( &g_csDataList );
  1460.  
  1461.     return S_OK;
  1462. }
  1463.  
  1464.  
  1465.  
  1466.  
  1467. //-----------------------------------------------------------------------------
  1468. // Name: AppendTextToEditControl()
  1469. // Desc: Appends a string of text to the edit control
  1470. //-----------------------------------------------------------------------------
  1471. VOID AppendTextToEditControl( HWND hDlg, TCHAR* strNewLogLine )
  1472. {
  1473.     static TCHAR strText[1024*10];
  1474.  
  1475.     HWND hEdit = GetDlgItem( hDlg, IDC_LOG_EDIT );
  1476.     SendMessage( hEdit, WM_SETREDRAW, FALSE, 0 );
  1477.     GetWindowText( hEdit, strText, 1024*9 );
  1478.  
  1479.     _tcscat( strText, strNewLogLine );
  1480.  
  1481.     int nSecondLine = 0;
  1482.     if( SendMessage( hEdit, EM_GETLINECOUNT, 0, 0 ) > 18 )
  1483.         nSecondLine = (int)SendMessage( hEdit, EM_LINEINDEX, 1, 0 );
  1484.  
  1485.     SetWindowText( hEdit, &strText[nSecondLine] );
  1486.  
  1487.     SendMessage( hEdit, WM_SETREDRAW, TRUE, 0 );
  1488.     InvalidateRect( hEdit, NULL, TRUE );
  1489.     UpdateWindow( hEdit );
  1490. }
  1491.  
  1492.  
  1493.  
  1494.  
  1495. //-----------------------------------------------------------------------------
  1496. // Name: UpdateConnectionInfo()
  1497. // Desc: Using the info buffer passed in,  display some interesting tidbits about
  1498. //       the connection.
  1499. //-----------------------------------------------------------------------------
  1500. VOID UpdateConnectionInfo( HWND hDlg, PDPN_CONNECTION_INFO pConnectionInfo,
  1501.                            DWORD dwHighMessages, DWORD dwHighBytes, 
  1502.                            DWORD dwNormalMessages, DWORD dwNormalBytes,
  1503.                            DWORD dwLowMessages, DWORD dwLowBytes )
  1504. {
  1505.     TCHAR strText[1024];
  1506.     int   nDrops;
  1507.     int   nSends;
  1508.  
  1509.     HWND hEdit = GetDlgItem( hDlg, IDC_CONNINFO_EDIT );
  1510.     int nThumbIndex = GetScrollPos( hEdit, SB_VERT );
  1511.  
  1512.     if( pConnectionInfo )
  1513.     {
  1514.         nDrops = pConnectionInfo->dwPacketsDropped + pConnectionInfo->dwPacketsRetried;
  1515.         nDrops *= 10000;
  1516.  
  1517.         nSends = pConnectionInfo->dwPacketsSentGuaranteed + pConnectionInfo->dwPacketsSentNonGuaranteed;
  1518.  
  1519.         if(nSends)
  1520.             nDrops /= nSends; // percent nDrops * 100
  1521.  
  1522.         _stprintf(strText, 
  1523.                            TEXT("Send Queue Messages High Priority=%d\r\n")       \
  1524.                            TEXT("Send Queue Bytes High Priority=%d\r\n")     \
  1525.                            
  1526.                            TEXT("Send Queue Messages Normal Priority=%d\r\n")       \
  1527.                            TEXT("Send Queue Bytes Normal Priority=%d\r\n")     \
  1528.  
  1529.                            TEXT("Send Queue Messages Low Priority=%d\r\n")       \
  1530.                            TEXT("Send Queue Bytes Low Priority=%d\r\n")     \
  1531.  
  1532.                            TEXT("Round Trip Latency MS=%dms\r\n")                  \
  1533.                            TEXT("Throughput BPS=%d\r\n")                         \
  1534.                            TEXT("Peak Throughput BPS=%d\r\n")                     \
  1535.                                                                             \
  1536.                            TEXT("Bytes Sent Guaranteed=%d\r\n")                   \
  1537.                            TEXT("Packets Sent Guaranteed=%d\r\n")                 \
  1538.                            TEXT("Bytes Sent Non-Guaranteed=%d\r\n")                \
  1539.                            TEXT("Packets Sent Non-Guaranteed=%d\r\n")              \
  1540.                                                                             \
  1541.                            TEXT("Bytes Retried Guaranteed=%d\r\n")        \
  1542.                            TEXT("Packets Retried Guaranteed=%d\r\n")      \
  1543.                            TEXT("Bytes Dropped Non-Guaranteed=%d\r\n")    \
  1544.                            TEXT("Packets Dropped Non-Guaranteed=%d\r\n")  \
  1545.                                                                             \
  1546.                            TEXT("Messages Transmitted High Priority=%d\r\n")       \
  1547.                            TEXT("Messages Timed Out High Priority=%d\r\n")          \
  1548.                            TEXT("Messages Transmitted Normal Priority=%d\r\n")     \
  1549.                            TEXT("Messages Timed Out Normal Priority=%d\r\n")        \
  1550.                            TEXT("Messages Transmitted Low Priority=%d\r\n")        \
  1551.                            TEXT("Messages Timed Out Low Priority=%d\r\n")           \
  1552.                                                                             \
  1553.                            TEXT("Bytes Received Guaranteed=%d\r\n")               \
  1554.                            TEXT("Packets Received Guaranteed=%d\r\n")             \
  1555.                            TEXT("Bytes Received Non-Guaranteed=%d\r\n")            \
  1556.                            TEXT("Packets Received Non-Guaranteed=%d\r\n")          \
  1557.                            TEXT("Messages Received=%d\r\n")                      \
  1558.                                                                             \
  1559.                            TEXT("Loss Rate=%d.%02d%%\r\n"),
  1560.                             
  1561.                            dwHighMessages, dwHighBytes,
  1562.                            dwNormalMessages, dwNormalBytes,
  1563.                            dwLowMessages, dwLowBytes,
  1564.  
  1565.                            pConnectionInfo->dwRoundTripLatencyMS, 
  1566.                            pConnectionInfo->dwThroughputBPS, 
  1567.                            pConnectionInfo->dwPeakThroughputBPS,
  1568.  
  1569.                            pConnectionInfo->dwBytesSentGuaranteed,
  1570.                            pConnectionInfo->dwPacketsSentGuaranteed,
  1571.                            pConnectionInfo->dwBytesSentNonGuaranteed,
  1572.                            pConnectionInfo->dwPacketsSentNonGuaranteed,
  1573.  
  1574.                            pConnectionInfo->dwBytesRetried,
  1575.                            pConnectionInfo->dwPacketsRetried,
  1576.                            pConnectionInfo->dwBytesDropped,
  1577.                            pConnectionInfo->dwPacketsDropped,
  1578.  
  1579.                            pConnectionInfo->dwMessagesTransmittedHighPriority,
  1580.                            pConnectionInfo->dwMessagesTimedOutHighPriority,
  1581.                            pConnectionInfo->dwMessagesTransmittedNormalPriority,
  1582.                            pConnectionInfo->dwMessagesTimedOutNormalPriority,
  1583.                            pConnectionInfo->dwMessagesTransmittedLowPriority,
  1584.                            pConnectionInfo->dwMessagesTimedOutLowPriority,
  1585.  
  1586.                            pConnectionInfo->dwBytesReceivedGuaranteed,
  1587.                            pConnectionInfo->dwPacketsReceivedGuaranteed,
  1588.                            pConnectionInfo->dwBytesReceivedNonGuaranteed,
  1589.                            pConnectionInfo->dwPacketsReceivedNonGuaranteed,
  1590.                            pConnectionInfo->dwMessagesReceived,
  1591.  
  1592.                            (nDrops/100), (nDrops % 100) );
  1593.     }
  1594.     else 
  1595.     {
  1596.         strText[0] = 0;
  1597.     }
  1598.  
  1599.     SetWindowText( hEdit, strText );  
  1600.     SendMessage( hEdit, EM_LINESCROLL, 0, nThumbIndex );
  1601. }
  1602.  
  1603.  
  1604.  
  1605.  
  1606. //-----------------------------------------------------------------------------
  1607. // Name: UpdateStats()
  1608. // Desc: 
  1609. //-----------------------------------------------------------------------------
  1610. VOID UpdateStats( HWND hDlg )
  1611. {
  1612.     HRESULT hr;
  1613.     static DWORD s_dwLastTime = timeGetTime();
  1614.     DWORD dwCurTime = timeGetTime();
  1615.  
  1616.     if( (dwCurTime - s_dwLastTime) < 200 )
  1617.         return;
  1618.  
  1619.     TCHAR strDataIn[32];
  1620.     TCHAR strDataOut[32];
  1621.  
  1622.     FLOAT fSecondsPassed = (dwCurTime - s_dwLastTime) / 1000.0f;
  1623.     FLOAT fDataIn  = g_dwDataRecieved / fSecondsPassed;
  1624.     FLOAT fDataOut = g_dwDataSent / fSecondsPassed;
  1625.  
  1626.     s_dwLastTime     = dwCurTime;
  1627.     g_dwDataRecieved = 0;
  1628.     g_dwDataSent     = 0;
  1629.  
  1630.     _stprintf( strDataIn,  TEXT("%0.1f BPS"), fDataIn );
  1631.     _stprintf( strDataOut, TEXT("%0.1f BPS"), fDataOut );
  1632.     SetDlgItemText( hDlg, IDC_ACTUAL_SEND_RATE, strDataOut );
  1633.     SetDlgItemText( hDlg, IDC_ACTUAL_RECIEVE_RATE, strDataIn );
  1634.  
  1635.     //  If a player has been selected for Connection Info Display we will handle it next
  1636.     if( g_pConnInfoTargetPlayer->dpnidPlayer != 0 )
  1637.     {
  1638.         // Call GetConnectionInfo and display results
  1639.         DPN_CONNECTION_INFO dpnConnectionInfo;
  1640.         ZeroMemory( &dpnConnectionInfo, sizeof(DPN_CONNECTION_INFO) );
  1641.         dpnConnectionInfo.dwSize = sizeof(DPN_CONNECTION_INFO);
  1642.         hr = g_pDP->GetConnectionInfo( g_pConnInfoTargetPlayer->dpnidPlayer, 
  1643.                                                    &dpnConnectionInfo,
  1644.                                                    0);
  1645.  
  1646.         if( SUCCEEDED(hr) )
  1647.         {
  1648.             DWORD dwHighMessages, dwHighBytes;
  1649.             DWORD dwNormalMessages, dwNormalBytes;
  1650.             DWORD dwLowMessages, dwLowBytes;
  1651.  
  1652.             hr = g_pDP->GetSendQueueInfo( g_pConnInfoTargetPlayer->dpnidPlayer, 
  1653.                                           &dwHighMessages, &dwHighBytes, 
  1654.                                           DPNGETSENDQUEUEINFO_PRIORITY_HIGH );
  1655.             if( FAILED(hr) )
  1656.                 DXTRACE_ERR( TEXT("GetSendQueueInfo"), hr );
  1657.  
  1658.             hr = g_pDP->GetSendQueueInfo( g_pConnInfoTargetPlayer->dpnidPlayer, 
  1659.                                      &dwNormalMessages, &dwNormalBytes, 
  1660.                                      DPNGETSENDQUEUEINFO_PRIORITY_NORMAL );
  1661.             if( FAILED(hr) )
  1662.                 DXTRACE_ERR( TEXT("GetSendQueueInfo"), hr );
  1663.  
  1664.             hr = g_pDP->GetSendQueueInfo( g_pConnInfoTargetPlayer->dpnidPlayer, 
  1665.                                      &dwLowMessages, &dwLowBytes, 
  1666.                                      DPNGETSENDQUEUEINFO_PRIORITY_LOW );
  1667.             if( FAILED(hr) )
  1668.                 DXTRACE_ERR( TEXT("GetSendQueueInfo"), hr );
  1669.  
  1670.             UpdateConnectionInfo( hDlg, &dpnConnectionInfo,
  1671.                                   dwHighMessages, dwHighBytes,
  1672.                                   dwNormalMessages, dwNormalBytes,
  1673.                                   dwLowMessages, dwLowBytes );
  1674.         }
  1675.         else
  1676.         {
  1677.             // If the player goes away, the set the target to none
  1678.             SendDlgItemMessage( hDlg, IDC_CONNINFO_COMBO, CB_SETCURSEL, 0, 0 );  
  1679.             ReadCombos( hDlg );
  1680.         }
  1681.     }
  1682.     else 
  1683.     {
  1684.         // Clear the conn info window if no connection is selected to display        
  1685.         UpdateConnectionInfo( hDlg, NULL, 0, 0, 0, 0, 0, 0 );
  1686.     }
  1687. }
  1688.  
  1689.  
  1690.